using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security.Cryptography;

namespace Toenda.Foundation.Security.Cryptography {
	/// <summary>
	/// Class RijndaelEncryption
	/// </summary>
	public class RijndaelEncryption {
		/// <summary>
		/// Default Ctor
		/// </summary>
		public RijndaelEncryption() {
		}

		/// <summary>
		/// Encrypt a string using a keyword
		/// </summary>
		/// <param name="clearText"></param>
		/// <param name="password"></param>
		/// <returns></returns>
		public static string EncryptString(string clearText, string password) {
			RijndaelEncryption crypt = new RijndaelEncryption();
			return crypt.Encrypt(clearText, password);
		}

		/// <summary>
		/// Decrypt a encrypted string using the correct keyword
		/// </summary>
		/// <param name="cipherText"></param>
		/// <param name="password"></param>
		/// <returns></returns>
		public static string DecryptString(string cipherText, string password) {
			RijndaelEncryption crypt = new RijndaelEncryption();
			return crypt.Decrypt(cipherText, password);
		}

		/// <summary>
		/// Encrypt a string using a keyword
		/// </summary>
		/// <param name="clearText"></param>
		/// <param name="password"></param>
		/// <returns></returns>
		public string Encrypt(string clearText, string password) {
			byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);

			PasswordDeriveBytes pdb = new PasswordDeriveBytes(
				password,
				new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }
			);

			byte[] encryptedData = this.Encrypt(
				clearBytes,
				pdb.GetBytes(32),
				pdb.GetBytes(16)
			);

			return Convert.ToBase64String(encryptedData);
		}

		/// <summary>
		/// Decrypt a encrypted string using the correct keyword
		/// </summary>
		/// <param name="cipherText"></param>
		/// <param name="password"></param>
		/// <returns></returns>
		public string Decrypt(string cipherText, string password) {
			byte[] cipherBytes = Convert.FromBase64String(cipherText);

			PasswordDeriveBytes pdb = new PasswordDeriveBytes(
				password,
				new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }
			);

			byte[] decryptedData = this.Decrypt(
				cipherBytes,
				pdb.GetBytes(32),
				pdb.GetBytes(16)
			);

			return Encoding.Unicode.GetString(decryptedData);
		}

		/// <summary>
		/// Internal encrypter
		/// </summary>
		/// <param name="clearText"></param>
		/// <param name="Key"></param>
		/// <param name="IV"></param>
		/// <returns></returns>
		private byte[] Encrypt(byte[] clearText, byte[] Key, byte[] IV) {
			MemoryStream ms = new MemoryStream();
			Rijndael alg = Rijndael.Create();
			alg.Key = Key;
			alg.IV = IV;

			CryptoStream cs = new CryptoStream(
				ms,
				alg.CreateEncryptor(),
				CryptoStreamMode.Write
			);

			cs.Write(clearText, 0, clearText.Length);
			cs.Close();

			byte[] encryptedData = ms.ToArray();

			return encryptedData;
		}

		/// <summary>
		/// Internal decrypter
		/// </summary>
		/// <param name="cipherData"></param>
		/// <param name="Key"></param>
		/// <param name="IV"></param>
		/// <returns></returns>
		private byte[] Decrypt(byte[] cipherData, byte[] Key, byte[] IV) {
			MemoryStream ms = new MemoryStream();
			Rijndael alg = Rijndael.Create();
			alg.Key = Key;
			alg.IV = IV;

			CryptoStream cs = new CryptoStream(
				ms,
				alg.CreateDecryptor(),
				CryptoStreamMode.Write
			);

			byte[] decryptedData = null;

			try {
				cs.Write(cipherData, 0, cipherData.Length);
				cs.Close();

				decryptedData = ms.ToArray();
			}
			catch(Exception) {
				decryptedData = new byte[0];
			}

			return decryptedData;
		}
	}
}
